package com.tiancheng.trade.settlement.aop;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.tiancheng.trade.settlement.util.IPUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;


@Component
@Aspect
@Slf4j
public class RequestAspect {

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 定义切点Pointcut
     */
    @Pointcut("execution(* com.tiancheng.trade.settlement.controller..*(..))")
    public void requestService() {
    }

    @Around("requestService()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) requestAttributes;
        HttpServletRequest request = sra.getRequest();

        // 获取请求uri、方法、类名等信息
        String remoteAddress = IPUtils.getProxyIP(request);
        String uri = request.getRequestURI();
        String methodName = pjp.getSignature().getName();
        String clazzName = pjp.getTarget().getClass().getSimpleName();
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        // 获取参数名称，参数值
        String[] parameterNames = methodSignature.getParameterNames();
        Object[] parameterValues = pjp.getArgs();
        StringBuilder paramBuilder = new StringBuilder();
        // 全局检查每个controller方法，判断输入参数是否为空
//        boolean isEmptyParams = isNullParams(parameterValues);
//        if (isEmptyParams) {
//            log.warn("request warn in aspect! request ip info: 【{}】 ⊙▽⊙ request url: 【{}】 ⊙▽⊙ request " +
//                            "Controller: 【{}】 ⊙▽⊙ request method: 【{}】 ⊙▽⊙ request param is all null!",
//                    remoteAddress, uri, clazzName, methodName);
//        }

        if (parameterNames != null && parameterNames.length > 0) {
            for (int i = 0; i < parameterNames.length; i++) {
                paramBuilder.
                        append("[").
                        append(parameterNames[i]).append("=").append(parameterValues[i])
                        .append("]");
            }
        }

        Object result;
        try {
            result = pjp.proceed();
        } catch (Throwable throwable) {
            log.error("request error in aspect! request ip info: 【{}】 ( ‵▽′)ψ  request url: 【{}】 ( ‵▽′)ψ request " +
                            "Controller: 【{}】 ( ‵▽′)ψ request method: 【{}】 ( ‵▽′)ψ  request param: {}",
                    remoteAddress, uri, clazzName, methodName, paramBuilder.toString());
            throw throwable;
        }
        String jsonString = "";
        if (result != null) {

            log.info("result:" + objectMapper.writeValueAsString(result));

        }
        long endTime = System.currentTimeMillis();
        long usedTime = endTime - startTime;

        log.info("request complete! request used time: 【{} ms】 o(^▽^)o request ip info: 【{}】 o(^▽^)o request url: " +
                        "【{}】 o(^▽^)o" +
                        " request Controller: 【{}】 o(^▽^)o " +
                        "request method: 【{}】 o(^▽^)o request param: 【{}】 o(^▽^)o response data: 【{}】",
                usedTime, remoteAddress, uri, clazzName, methodName, paramBuilder.toString(), jsonString);
        return result;
    }


    /**
     * 如果params所有元素都为null，则返回true，否则返回false
     *
     * @param params
     * @return
     */
    private boolean isNullParams(Object[] params) {
        if (params.length <= 0) {
            return true;
        }
        boolean isEmpty = true;
        for (Object param : params) {
            if (param != null) {
                isEmpty = false;
                break;
            }
        }
        return isEmpty;
    }
}
